package org.guolm;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @author guolm
 * @version 1.0
 * @className HelloXClassLoader.java
 * @description Hello.xlass ClassLoader
 * @createTime 2021-06-27 21:16:36
 */
public class HelloXClassLoader extends ClassLoader {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InstantiationException, InvocationTargetException {
        HelloXClassLoader xClassLoader = new HelloXClassLoader();
        Class<?> hellClass = xClassLoader.findClass("Hello");
        Method helloMethod = hellClass.getDeclaredMethod("hello");
        Object helloInstance = hellClass.newInstance();
        helloMethod.invoke(helloInstance);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        InputStream inputStream = null;
        try {
            inputStream = this.getClass().getClassLoader().getResourceAsStream(name + ".xlass");
            if (inputStream == null) {
                throw new ClassNotFoundException(name);
            }
            byte[] sources = inputStreamToByteArray(inputStream);
            byte[] decodeBytes = decode(sources);
            return defineClass(name, decodeBytes, 0, decodeBytes.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name, e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // do nothing
                }
            }
        }
    }

    /**
     * 解码源字节数组 255 - original byte
     *
     * @param sources 源文件字节数组
     * @return
     */
    private byte[] decode(byte[] sources) {
        assert sources != null && sources.length > 0;
        byte[] decodeBytes = new byte[sources.length];
        for (int i = 0; i < sources.length; i++) {
            decodeBytes[i] = (byte) (0xff - sources[i]);
        }
        return decodeBytes;
    }

    /**
     * inputStream 转换为 byte array
     *
     * @param inputStream 源文件输入流
     * @return
     * @throws IOException
     */
    private byte[] inputStreamToByteArray(InputStream inputStream) throws IOException {
        int byteLength = inputStream.available();
        byte[] byteArray = new byte[byteLength];
        assert inputStream.read(byteArray) != -1;
        return byteArray;
    }
}
